
// Extends a HTML DOM element with an ID attribute to an entity view element.
// @param element HTML DOM element to extend.
EntityView = function(entityViewDom) {
	this.dom = entityViewDom;
	if (!this.dom.entityView)
		this.dom.entityView = this;
	EntityView.scanIdAttrValue(this.dom.element, this);
	if (!this.objId) {
		var e = this.dom.element;
		while (e = e.parentNode) {
			var v = EntityView.scanIdAttrValue(e);
			if (v && v.objId) {
				this.objId = v.objId;
				this.scope = v.scope;
				break;
			}
		}
	}
	if (!this.objId)
		alert("No object ID found for entity view '" + this.dom.element.id + "'");
	this.entity = new Entity(this.objId, this.id, this.scope);
	this.v = this.dom.getVector();
	this.s = this.dom.getSize();
}

EntityView.scanIdAttrValue = function(e, r) {
	if (!e || !e.id || !e.id.indexOf)
		return;
	var v = e.id;
	// hidden ID
	if (v.indexOf("_") == 0)
		return;
	// view num
	var p = v.indexOf("~");
	if (p >= 0)
		v = v.substring(0, p);
	// node ID, object ID
	if (!r)
		r = new Object();
	p = v.indexOf("#");
	if (p >= 0)
		r.id = v.substring(p + 1);
	else if (v.indexOf("/") < 0)
		r.id = v;
	else if (v.length > 0)
		r.objId = v;
	if (p > 0)
		r.objId = v.substring(0, p);
	if (r.objId) {
		var accessScopes = ["content", "meta", "meta2", "cosmos", "history", "view", "data", "cache"];
		for (var i = 0; i < accessScopes.length; ++i) {
			var s = accessScopes[i];
			p = r.objId.lastIndexOf("." + s);
			if ((p > 0) && ((p + s.length + 1) == r.objId.length)) {
				r.scope = s;
				r.objId = r.objId.substring(0, p);
				break;
			}
		}
	}
	return r;
}

EntityView.prototype.getViewObjId = function() {
	var viewObjId = this.dom.element.ownerDocument.documentElement.id;
	if (!viewObjId) {
		viewObjId = this.dom.element.ownerDocument.location.href.substring(BasePath.get().length);
		var p = viewObjId.indexOf("?");
		if (0 < p)
			viewObjId = viewObjId.substring(0, p);
		p = viewObjId.lastIndexOf(".");
		if (0 < p)
			viewObjId = viewObjId.substring(0, p);
	}
	return viewObjId;
}

// Selects this entity view element for editing.
// @param active Boolean to select/unselect.
// @return -
EntityView.prototype.selectForEdit = function(active, scrollIntoView) {
	var paper = this.dom.element.getElementsByTagName("div")[0];
	if (paper && ("paper" == paper.className))
		EntityView._markElement(Dom(paper), active, "ed_select");
	else
		EntityView._markElement(this.dom, active, "ed_select");
	try {
		if (active) {
			// this.dom.element.setAttribute("tabIndex", "-1");
			// this.dom.element.focus();
			if (scrollIntoView) {
				var v = this.dom.getVector();
				var s = this.dom.getScrollVector();
				var w = Wnd(this.dom);
				var d = [v[0] - s[0], v[1] - s[1]];
				if ((d[0] < 0) || (d[0] > w.wnd.innerWidth) || (d[1] < 0) || (d[1] > w.wnd.innerHeight))
					this.dom.element.scrollIntoView();
			}
		}
		else {
			// this.dom.element.removeAttribute("tabIndex");
			// this.dom.element.blur();
		}
	}
	catch (ex) {
	}
}

// Marks this entity view element for being processed.
// @param doMark Boolean to mark/unmark.
// @return -
EntityView.prototype.markInProcess = function(doMark) {
	EntityView._markElement(this.dom, doMark, "ed_process");
}

// Marks an element.
// @param element Element to mark.
// @param doMark Boolean to mark/unmark.
// @return -
EntityView._markElement = function(dom, doMark, style) {
	if (doMark)
		dom.addStyleClass(style);
	else
		dom.removeStyleClass(style);
	if ((dom.element.nodeName == "SPAN") || (dom.element.nodeName == "span")) {
		var tds = dom.element.getElementsByTagName("td");
		for (var i = 0; i < tds.length; ++i) {
			var e = tds[i];
			EntityView._markElement(Dom(e), doMark, style);
		}
	}
}

// Access to this parent entity view element.
// @return The parent entity view element or 'null', if no parent is available.
EntityView.prototype.getParent = function() {
	var parent = this.dom.element.parentNode;
	while (parent) {
		if (EntityView.isEntityViewElement(parent))
			return Dom(parent).getEntityView();
		parent = parent.parentNode;
	}
	return null;
}

// Access to this first child entity view element.
// @return The first child entity view element or 'null', if no child is
// available.
EntityView.prototype.getFirstChild = function() {
	return EntityView.getFirstChildEntityView(this.dom.element);
}

EntityView.getFirstChildEntityView = function(node) {
	var children = node.childNodes;
	for (var i = 0; i < children.length; i++) {
		var child = children[i];
		if (EntityView.isEntityViewElement(child))
			return Dom(child).getEntityView();
		child = EntityView.getFirstChildEntityView(child);
		if (child)
			return child;
	}
	return null;
}

// Access to this last child entity view element.
// @return The last child entity view element or 'null', if no child is
// available.
EntityView.prototype.getLastChild = function() {
	return EntityView.getLastChildEntityView(this.dom.element);
}

EntityView.getLastChildEntityView = function(node) {
	var children = node.childNodes;
	for (var i = children.length - 1; i >= 0; i--) {
		var child = children[i];
		if (EntityView.isEntityViewElement(child))
			return Dom(child).getEntityView();
		child = EntityView.getLastChildEntityView(child);
		if (child)
			return child;
	}
	return null;
}

// Access to the child entity view elements.
// @return The child entity view elements or 'null', if no child is available.
EntityView.prototype.getChildren = function() {
	var entityViews = new Array();
	EntityView._getChildEntityViews(this.dom.element, entityViews);
	return entityViews;
}

EntityView._getChildEntityViews = function(node, entityViews) {
	var children = node.childNodes;
	for (var i = 0; i < children.length; i++) {
		var child = children[i];
		if (EntityView.isEntityViewElement(child))
			entityViews[entityViews.length] = Dom(child).getEntityView();
		else
			EntityView._getChildEntityViews(child, entityViews);
	}
}

// Access to this previous sibling entity view element.
// @return The previous sibling entity view element or 'null', if no previous
// sibling is available.
EntityView.prototype.getPreviousSibling = function() {
	var sibling = this.dom.element.previousSibling;
	while (sibling) {
		if (EntityView.isEntityViewElement(sibling))
			return Dom(sibling).getEntityView();
		sibling = sibling.previousSibling;
	}
	var parentEntityView = this.getParent();
	if (null == parentEntityView)
		return null;
	sibling = null;
	var childEntityViews = parentEntityView.getChildren();
	for (var i = 0; i < childEntityViews.length; ++i) {
		var c = childEntityViews[i];
		if (c == this)
			return sibling;
		sibling = c;
	}
	return null;
}

// Access to this next sibling entity view element.
// @return The next sibling entity view element or 'null', if no next sibling is
// available.
EntityView.prototype.getNextSibling = function() {
	var sibling = this.dom.element.nextSibling;
	while (sibling) {
		if (EntityView.isEntityViewElement(sibling))
			return Dom(sibling).getEntityView();
		sibling = sibling.nextSibling;
	}
	var parentEntityView = this.getParent();
	if (null == parentEntityView)
		return null;
	var childEntityViews = parentEntityView.getChildren();
	sibling = childEntityViews[0];
	for (var i = 1; i < childEntityViews.length; ++i) {
		var c = childEntityViews[i];
		if (sibling == this)
			return c;
		sibling = c;
	}
	return null;
}

EntityView.prototype.getPrevious = function() {
	var ev = this.getPreviousSibling();
	while (null != ev) {
		var c = ev.getChildren();
		if (c.length > 0)
			ev = c[c.length - 1];
		else
			return ev;
	}
	return this.getParent();
}

EntityView.prototype.getNext = function() {
	var n = this.getFirstChild();
	if (null != n)
		return n;
	n = this.getNextSibling();
	if (null != n)
		return n;
	var p = this.getParent();
	while (null != p) {
		n = p.getNextSibling();
		if (null != n)
			return n;
		p = p.getParent();
	}
	return null;
}

EntityView.getEntityViews = function() {
	var evs = new Array();
	var es = document.body.getElementsByTagName("*");
	for (var i = 0; i < es.length; ++i) {
		var e = es[i];
		if (!EntityView.isEntityViewElement(e))
			continue;
		evs.push(Dom(e).getEntityView());
	}
	return evs;
}

// get the top most centered entity view currently visible (scrolling)
EntityView.getVisualTopCenter = function(evs) {
	if (!evs)
		evs = EntityView.getEntityViews();
	var b = Dom(document.body);
	var sl = b.getScrollVector();
	var sz = [window.innerWidth, window.innerHeight];
	// the visual center (a 5th to the top, a 3rd to the left)
	var vC = [sl[0] + sz[0] / 3, sl[1] + sz[1] / 5];
	var dMin;
	var evC = null;
	for (var i = 0; i < evs.length; ++i) {
		var ev = evs[i];
		if ((ev.v[0] > (sl[0] + sz[0])) || ((ev.v[0] + ev.s[0]) < sl[0]) || (ev.v[1] > (sl[1] + sz[1]))
				|| ((ev.v[1] + ev.s[1]) < sl[1]))
			// ignore invisible views
			continue;
		var v = [ev.v[0] + ev.s[0] / 2, ev.v[1] + ev.s[1] / 2];
		var vD = [v[0] - vC[0], v[1] - vC[1]];
		var d = Math.sqrt(Math.pow(vD[0], 2) + Math.pow(vD[1], 2));
		// prefer line based views
		d = d - ev.s[0] / ev.s[1];
		// prefer medium sized views (50 kPixel; log(50) = 1.7)
		var a = ev.s[0] * ev.s[1] / 1000;
		d = d + Math.pow((Math.log(a) / Math.LN10 - 1.7), 2) * 100;
		if ((i == 0) || (d < dMin)) {
			evC = ev;
			dMin = d;
		}
	}
	return evC;
}

EntityView.prototype.getLeft = function(evs, vFactor) {
	if (!evs)
		evs = EntityView.getEntityViews();
	if (!vFactor)
		vFactor = 1;
	var dMin = -1;
	var evC = null;
	for (var i = 0; i < evs.length; ++i) {
		var ev = evs[i];
		if (ev == this)
			continue;
		var d = this.v[0] - ev.v[0] - ev.s[0];
		if (0 > d)
			continue;
		d += Math.abs(this.v[1] - ev.v[1]) * vFactor;
		if ((0 > dMin) || (d < dMin)) {
			evC = ev;
			dMin = d;
		}
	}
	return evC;
}

EntityView.prototype.getRight = function(evs, vFactor) {
	if (!evs)
		evs = EntityView.getEntityViews();
	if (!vFactor)
		vFactor = 1;
	var dMin = -1;
	var evC = null;
	for (var i = 0; i < evs.length; ++i) {
		var ev = evs[i];
		if (ev == this)
			continue;
		var d = ev.v[0] - this.v[0] - this.s[0];
		if (0 > d)
			continue;
		d += Math.abs(this.v[1] - ev.v[1]) * vFactor;
		if ((0 > dMin) || (d < dMin)) {
			evC = ev;
			dMin = d;
		}
	}
	return evC;
}

EntityView.prototype.getUp = function(evs, hFactor) {
	if (!evs)
		evs = EntityView.getEntityViews();
	if (!hFactor)
		hFactor = 1;
	var dMin = -1;
	var evC = null;
	for (var i = 0; i < evs.length; ++i) {
		var ev = evs[i];
		if (ev == this)
			continue;
		var d = this.v[1] - ev.v[1];// - ev.s[1];
		if ((1 > d) && !ev.dom.isAncestorOf(this.dom.element))
			continue;
		d += Math.abs(this.v[0] - ev.v[0]) * hFactor;
		if ((0 > dMin) || (d <= dMin)) {
			evC = ev;
			dMin = d;
		}
	}
	return evC;
}

EntityView.prototype.getDown = function(evs, hFactor) {
	if (!evs)
		evs = EntityView.getEntityViews();
	if (!hFactor)
		hFactor = 1;
	var dMin = -1;
	var evC = null;
	for (var i = 0; i < evs.length; ++i) {
		var ev = evs[i];
		if (ev == this)
			continue;
		var isChild = ev.dom.hasAncestor(this.dom.element);
		var d = ev.v[1] - this.v[1];// - this.s[1];
		if ((1 > d) && !isChild)
			continue;
		if (!isChild)
			d += this.s[0] + this.s[1];
		d += Math.abs(this.v[0] - ev.v[0]) * hFactor;
		if ((0 > dMin) || (d < dMin)) {
			evC = ev;
			dMin = d;
		}
	}
	return evC;
}

// TODO: rename
EntityView.prototype.contains = function(element) {
	return this.dom.element == EntityView.getEntityViewElement(element);
}

// Builds the markup for the entity view ancestor path for this entity view.
// @param fnName the name of the JavaScript function to call, when an ancestor
// has been clicked.
EntityView.prototype.getActiveEntityPath = function() {
	var entityView = this;
	var path = entityView.dom.element.id;
	while ((entityView.dom.element.className != "contentPanel")
			&& (entityView.dom.element.tagName != "body")) {
		entityView = entityView.getParent();
		if (!entityView)
			break;
		if ((entityView.dom.element.className == "contentPanel")
				|| (entityView.dom.element.tagName == "body"))
			path += "<br/>";
		path = "<span onclick=\"Doc().selectionSupport.select(Dom('" + entityView.dom.element.id
				+ "').getEntityView())\" class=\"link\">" + entityView.dom.element.id + "</span> - " + path;
	}
	return path;
}

// Check a node to be an entity view element.
// @param node The node to be checked.
// @return Boolean to result the check.
EntityView.isEntityViewElement = function(node) {
	// an entity view element holds the id of the represented entity.
	return (typeof(node.id) != "undefined") && (node.id.length > 0) && (node.id.charAt(0) != '_')
			&& (node != node.ownerDocument.documentElement);
}

// Determines the entity view element for a given node. Its the first entity
// view element along the
// ancestor axis.
// @param node The node to determine the entity view element for.
// @return The entity view element containing the given node.
EntityView.getEntityViewElement = function(node) {
	while (node) {
		if (EntityView.isEntityViewElement(node))
			return node;
		node = node.parentNode;
	}
	return null;
}

E.Dom.prototype.getEntityView = function() {
	if (!this.entityView) {
		var evEl = EntityView.getEntityViewElement(this.element);
		if (evEl) {
			var d = Dom(evEl);
			d.entityView = new EntityView(d);
			this.entityView = d.entityView;
		}
	}
	return this.entityView;
}
